package com.ht.api.open;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import com.ht.api.constant.CacheConstants;
import com.ht.api.db.Entity;
import com.ht.api.db.exception.HtException;
import com.ht.api.db.remote.ApiUtil;
import com.ht.api.db.util.FunctionUtil;
import com.ht.api.db.util.HtUtil;
import com.ht.api.db.util.RedisUtil;
import com.ht.api.db.util.WxFileUtils;
import com.ht.api.open.paging.IPaging;
import com.ht.api.util.FileProcessor;
import com.ht.api.util.ParamParser;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 对接外部开放平台的工具类
 * @author asq
 * @createTime 2024年12月17日 09:42:20
 */
@Slf4j
public class OpenUtil {
	/** 上传文件时的前端参数名 */
	public static final String PARAM_OSS_URL = "oss_url";
	/** 获取token的服务类 */
	private static TokenService tokenService = SpringUtil.getBean(TokenService.class);
	
	/**
	 * 调用外部开放平台的api接口（debug模式）
	 * @param appId 应用ID
	 * @param apiId 接口ID
	 * @param param 参数
	 * @return
	 */
	public static String callByDebug(String appId, long apiId, JSONObject param) {
		return call(appId, apiId, param, false, true, true);
	}
	
	/**
	 * 调用外部开放平台的api接口（不组装参数）
	 * @param appId 应用ID
	 * @param apiId 接口ID
	 * @param param 参数
	 * @return
	 */
	public static String callByNoAssemble(String appId, long apiId, JSONObject param) {
		return call(appId, apiId, param, true, false, false);
	}
	
	/**
	 * 调用外部开放平台的api接口
	 * @param appId 应用ID
	 * @param apiId 接口ID
	 * @param param 参数
	 * @return
	 */
	public static String call(String appId, long apiId, JSONObject param) {
		return call(appId, apiId, param, true, false, true);
	}
	
	/**
	 * 调用外部开放平台的api接口
	 * @param appId 应用ID
	 * @param apiId 接口ID
	 * @param param 参数
	 * @param pageable 是否递归分页
	 * @return
	 */
	public static String call(String appId, long apiId, JSONObject param, boolean pageable) {
		return call(appId, apiId, param, pageable, false, true);
	}
	
	/**
	 * 调用外部开放平台的api接口
	 * @param appId 应用ID
	 * @param apiId 接口ID
	 * @param param 参数
	 * @param pageable 是否递归分页
	 * @param pageable 是否是debug
	 * @param isFormatParam 是否处理参数
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static String call(String appId, long apiId, JSONObject param, boolean pageable, 
			boolean isDebug, boolean isFormatParam) {
		try {
			// 1. 查询api接口
			Entity api = RedisUtil.get(CacheConstants.KEY_API + apiId);
			if (api == null) {
				throw new RuntimeException("找不到api接口，apiId:" + apiId);
			}
			// 查询app应用
			Entity app = RedisUtil.get(CacheConstants.KEY_APP + appId);
			if (app == null) {
				throw new RuntimeException("找不到app应用，appId:" + appId);
			}
			
			// 2. 获取所属开放平台的config配置
			long platformId = app.getLong("pid");
			JSONObject platformConfig = getPlatformConfig(platformId);
			
			// 3. 获取token
			String token = tokenService.getToken(platformId, appId);
			
			// 4. 组装api的各项配置
			JSONObject apiConfig = api.getJSONObject("config");
			if (apiConfig.get("token_name") == null) {
				apiConfig.set("token_name", platformConfig.getStr("token_name"));
			}
			if (api.get("token_at") == null) {
				apiConfig.set("token_at", platformConfig.getStr("at"));
			}
			apiConfig.set("token_path", platformConfig.getStr("path"));
			
			// 重新组装一下参数
			if (isFormatParam) {
				Map<String, Object> map = param.toBean(Map.class);
				param = new ParamParser().assembleJson(apiConfig.getJSONArray("param"), map);
			}
			
			// 5. 调用接口
			String result = callByPaging(apiConfig, token, param, null, pageable);
			if (isDebug) {
				// 组装
				JSONObject json = JSONUtil.createObj();
				json.set("request", param.set("tonken", token));
				json.set("response", JSONUtil.parse(result));
				return JSONUtil.toJsonStr(json);
			} else {
				return result;
			}
		} catch (Exception e) {
			log.error("[OpenUtil]调用开放平台接口发生异常：" + e.getMessage(), e);
			return null;
		}
	}
	
	/**
	 * 调用外部开放平台的api接口
	 * @param apiConfig api规则配置
	 * @param token access_token
	 * @param param 请求参数
	 * @param curDataStr 当前页数据（根据当前页数据计算下一页参数，具体计算规则见{@link IPaging}接口的各个实现类
	 * @param pageable 是否递归分页
	 * @return
	 */
	private static String callByPaging(JSONObject apiConfig, String token, JSONObject param, String curDataStr, boolean pageable) {
		// 1. 注入分页参数
		JSONObject curParam = param;
		if (pageable) {
			JSONObject page = getPaging(apiConfig, curDataStr);
			if (page != null) {
				page.forEach(entry -> curParam.set(entry.getKey(), entry.getValue()));
			}
		}
		
		// 2. 调用接口
		String result = call(apiConfig, token, curParam);
		
		// 3. 如果需要分页，则递归调用
		boolean pageableConfig = HtUtil.hasPath(apiConfig, "paging.pageable") && 
				apiConfig.getByPath("paging.pageable", Boolean.class);
		if (pageable && pageableConfig) {
			// 获取分页的各项配置
			String pagingClsName = apiConfig.getByPath("paging.func", String.class);
			String arrDataPath = apiConfig.getByPath("paging.data_path", String.class);
			int limit = apiConfig.getByPath("paging.limit", Integer.class);
			
			// 解析当前页数据
			JSONObject curData = JSONUtil.parseObj(result);
			
			// 获取分页器实现类
			IPaging paging = FunctionUtil.getFunction(pagingClsName);
			if (paging.hasNext(curData, arrDataPath, limit)) {
				// 调用下一页的接口
				String nextResult = callByPaging(apiConfig, token, param, result, pageable);
				
				// 将下一页数据合并到本页
				JSONObject nextData = JSONUtil.parseObj(nextResult);
				JSONArray curDataArr = curData.getByPath(arrDataPath, JSONArray.class);
				JSONArray nextDataArr = nextData.getByPath(arrDataPath, JSONArray.class);
				nextDataArr.forEach(curDataArr::add);
				
				return curData.toString();
			}
		}
		
		return result;
	}
	
	/**
	 * 根据当前页数据获取下一页的分页对象
	 * @param apiConfig api配置规则
	 * @param curDataStr 当前页数据
	 * @return
	 */
	private static JSONObject getPaging(JSONObject apiConfig, String curDataStr) {
		boolean pageable = HtUtil.hasPath(apiConfig, "paging.pageable") && 
				apiConfig.getByPath("paging.pageable", Boolean.class);
		if (pageable) {
			// 获取分页的各项配置
			String pagingClsName = apiConfig.getByPath("paging.func", String.class);
			String arrDataPath = apiConfig.getByPath("paging.data_path", String.class);
			String limitName = apiConfig.getByPath("paging.limit_name", String.class);
			int limit = apiConfig.getByPath("paging.limit", Integer.class);

			// 解析当前页数据
			JSONObject curData = null;
			if (curDataStr != null) curData = JSONUtil.parseObj(curDataStr);
			
			// 获取分页器实现类
			IPaging paging = FunctionUtil.getFunction(pagingClsName);
			if (paging.hasNext(curData, arrDataPath, limit)) {
				// 获取下一页的分页对象
				limitName = StrUtil.isBlank(limitName) ? "limit" : limitName;
				return paging.getPaging(curData, limit, limitName);
			}
		}
		
		return null;
	}
	
	/**
	 * 调用开放平台接口
	 * @param apiConfig 接口配置json
	 * @param token  access_token
	 * @param param 参数json
	 * @return
	 */
	private static String call(JSONObject apiConfig, String token, JSONObject param) {
		// 获取api信息
		String url = apiConfig.getStr("url");
		String requestType = apiConfig.getStr("request_type");
		String tokenName = apiConfig.getStr("token_name");
		String tokenPath = apiConfig.getStr("token_path");
		String tokenAt = apiConfig.getStr("token_at");
		
		// 构造请求接口的参数
		Method httpMethod = "GET".equalsIgnoreCase(requestType) ? Method.GET : Method.POST;
		JSONObject header = JSONUtil.createObj();
		JSONObject urlParams = JSONUtil.createObj();
		if ("BODY".equalsIgnoreCase(tokenAt)) {
			// body方式：把token放在body请求参数中
			param.putByPath(tokenPath, token);
		} else if ("HEADER".equalsIgnoreCase(tokenAt)){
			// header方式：把token放在header中
			header.set(tokenName, token);
		} else {
			// url方式：把token放在url参数中
			urlParams.set(tokenName, token);
		}
		
		// 请求接口
		if ("FILE".equalsIgnoreCase(requestType)) {
			// 获取上传文件的url
	        String finalUrl = url;
	        if (urlParams != null && !urlParams.isEmpty()) {
	            finalUrl = HttpUtil.urlWithForm(url, urlParams, StandardCharsets.UTF_8, true);
	        }
	      	if (param != null) {
	      		finalUrl = HttpUtil.urlWithForm(finalUrl, param, StandardCharsets.UTF_8, true);
        	}
	        
	    	// 下载阿里云文件
	      	String ossUrl = param.getStr(PARAM_OSS_URL);
	    	File file = WxFileUtils.downFile(ossUrl);
			JSONArray rules = apiConfig.getJSONArray("rules");
			try {
				if (rules != null && !rules.isEmpty()) {
					file = FileProcessor.processFile(file, rules);
				}
			} catch (Exception e) {
				log.error("[OpenUtil]处理文件发生异常：{}", e.getMessage(), e);
				throw new HtException("处理文件失败~");
			}

	        // 上传文件
			return ApiUtil.uploadFile(finalUrl, file);
		} else {
			return ApiUtil.sendRequest(url, httpMethod, header, urlParams, param);
		}
	}
	
	/**
	 * 获取平台配置
	 * @param appid 应用ID
	 * @return
	 */
	private static JSONObject getPlatformConfig(long pid) {
		return RedisUtil.get(CacheConstants.KEY_PLATFORM + pid);
	}
}
